﻿using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System;




namespace ScrumFactory.Services.Logic {

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    [Export(typeof(IAuthorizationService))]
    public class AuthorizationService : IAuthorizationService {

        static Dictionary<string, AuthorizationInfo> authorizedTokens = new Dictionary<string,AuthorizationInfo>();

        [Import(typeof(ITeamService))]
        private ITeamService teamService { get; set; }

        [Import(typeof(IProjectsService))]
        private IProjectsService projectsService { get; set; }

        [ImportMany(typeof(IAuthorizationProviderService))]
        private IEnumerable<IAuthorizationProviderService> authorizationProviders { get; set; }

        [WebInvoke(Method = "POST", UriTemplate = "SignedMembers/{providerName}/{user}/Token", RequestFormat = WebMessageFormat.Json)]
        public MemberProfile SignInMember(string providerName, string user, string token) {

            user = user.Replace("@@", "\\");

            // if the token is not authenticated yet, validate it
            AuthorizationInfo info;
            if (!authorizedTokens.ContainsKey(token))
                info = CreateAuthorizationForToken(providerName, user, token);
            else
                info = authorizedTokens[token];

            // token was not valid
            if (info == null)
                return null;

            // if the token is not from this user
            if (info.User != user)
                return null;

            // gets the user
            MemberProfile member = GetOrCreateMember(providerName, user);
            if (member == null)
                return null;
            info.MemberUId = member.MemberUId;

            return member;
        }

        public void VerifyRequestAuthorizationToken() {

            var token = SignedMemberToken;

            if (!authorizedTokens.ContainsKey(token))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

        }

        private void GetTokenFromRequest(out string providerName, out string token) {
            string auth = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
            if (auth == null)
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            string[] parts = auth.Split('=');
            if (parts.Length != 2)
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            providerName = parts[0].Replace("auth","").Trim();
            token = parts[1].Trim().Replace("\n", "");

        }


        [WebInvoke(Method = "DELETE", UriTemplate = "SignedMembers/{providerName}/{user}", RequestFormat = WebMessageFormat.Json)]
        public void SignOutMember(string providerName, string user) {
            user = user.Replace("@@", "\\");

            // if is not logged get out
            if (!authorizedTokens.ContainsKey(SignedMemberToken))
                return;

            AuthorizationInfo info = authorizedTokens[SignedMemberToken];
            if(info==null || info.ProviderName!=providerName || info.User!=user)
                throw new WebFaultException(System.Net.HttpStatusCode.BadRequest);
            authorizedTokens.Remove(SignedMemberToken);
        }

        public MemberProfile SignedMemberProfile {
            get {
                string token = SignedMemberToken;
                if (!authorizedTokens.ContainsKey(token))
                    throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

                AuthorizationInfo info = authorizedTokens[SignedMemberToken];                
                return teamService.GetMember(info.MemberUId);
            }
        }

        public string SignedMemberToken {
            get {
                string provider;
                string token;
                GetTokenFromRequest(out provider, out token);
                return token;
            }
        }

        private IAuthorizationProviderService GetAuthorizationProvider(string providerName) {
            return authorizationProviders.SingleOrDefault(p => p.ProviderName == providerName);
        }

        private string DefaultCompanyName {
            get {
                return System.Configuration.ConfigurationManager.AppSettings["DefaultCompanyName"];
            }
        }

        private MemberProfile GetOrCreateMember(string providerName, string user) {
            MemberProfile member = teamService.GetMember(user);

            if (member == null) {
                member = new MemberProfile();
                member.MemberUId = user;
                member.FullName = user;
                member.IsFactoryOwner = false;
                member.CanSeeProposalValues = false;
                member.CompanyName = DefaultCompanyName;
                member.AuthorizationProvider = providerName;                
                teamService.CreateMember(member);
            }
            
            return member;
                
        }

        private AuthorizationInfo CreateAuthorizationForToken(string providerName, string user, string token) {

            // no provider to validate the token
            IAuthorizationProviderService provider = GetAuthorizationProvider(providerName);
            if (provider == null)
                return null;

            // not a valid token
            if (!provider.ValidadeToken(token, user))
                return null;

            // so the token is valid, at it the valid tokens
            AuthorizationInfo info = new AuthorizationInfo();
            info.ProviderName = providerName;
            info.User = user;
            info.Token = token;
            info.IssueDate = DateTime.Now;

            authorizedTokens.Add(token.Trim().Replace("\n", ""), info);

            return info;
        }


        public void VerifyUser(string memberUId) {

            if (!authorizedTokens.ContainsKey(SignedMemberToken))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            if(SignedMemberProfile.MemberUId!=memberUId)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public void VerifyUserOrPermissionAtProject(string memberUId, string projectUId, PermissionSets permission) {
            if (!projectsService.MemberHasPermissionAtProject(SignedMemberProfile.MemberUId, projectUId, new PermissionSets[] {  permission })
                && SignedMemberProfile.MemberUId != memberUId)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public void VerifyPermissionAtProject(string projectUId, PermissionSets permission) {
            PermissionSets[] ps = new PermissionSets[1];
            ps[0] = permission;
            VerifyPermissionAtProject(projectUId, ps);
        }

        public void VerifyPermissionAtProject(string projectUId, PermissionSets[] permissions) {

            string token = SignedMemberToken;
            if (!authorizedTokens.ContainsKey(token))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            string memberUId = authorizedTokens[token].MemberUId;
            
            bool hasPermission = projectsService.MemberHasPermissionAtProject(memberUId, projectUId, permissions);
            if(!hasPermission)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public void VerifyFactoryOwner() {
            
            if (!authorizedTokens.ContainsKey(SignedMemberToken))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            if(!SignedMemberProfile.IsFactoryOwner)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public void VerifyCanSeeProposalValues() {

            if (!authorizedTokens.ContainsKey(SignedMemberToken))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            if (!SignedMemberProfile.CanSeeProposalValues)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public bool IsProjectScrumMaster(string projectUId) {
            return projectsService.MemberHasPermissionAtProject(
                SignedMemberProfile.MemberUId,
                projectUId,
                new PermissionSets[] { PermissionSets.SCRUM_MASTER });
        }

        protected class AuthorizationInfo {

            public string ProviderName;
            public string User;
            public string Token;
            public string MemberUId;
            public DateTime IssueDate;
        }

    }

    

    
}
